﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Web;
using System.IO;
using System.Drawing.Imaging;
using System.Threading;
using System.Net;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Web.UI.WebControls;
using System.Reflection;
using System.Web.UI;

namespace Tilver.Web
{
    /// <summary>
    /// Web辅助类
    /// </summary>
    public static class WebUtil
    {
        #region 输出图片
        /// <summary>
        /// 输出图片
        /// </summary>
        /// <param name="data">图片数据</param>
        public static void ResponseImage(byte[] data, HttpContext context = null)
        {
            HttpResponse response = (context == null) ? HttpContext.Current.Response : context.Response;
            response.ClearContent();
            response.ContentType = "image/Jpeg";
            response.BinaryWrite(data);
            response.Flush();
            response.End();
        }

        /// <summary>
        /// 输出图片
        /// </summary>
        /// <param name="img">图片</param>
        public static void ResponseImage(System.Drawing.Image img, HttpContext context = null)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                img.Save(ms, ImageFormat.Jpeg);
                ResponseImage(ms.ToArray(), context);
            }
        }

        /// <summary>
        /// 输出图片
        /// </summary>
        /// <param name="filename">包含WEB相对路径或绝对路径的文件名</param>
        public static void ResponseImage(string filename, HttpContext context = null)
        {
            context = context ?? HttpContext.Current;
            filename = context.Server.MapPath(filename);
            using (FileStream fs = new FileStream(filename, FileMode.Open))
            {
                byte[] data = new byte[fs.Length];
                fs.Read(data, 0, (int)fs.Length);
                ResponseImage(data, context);
            }
        }

        /// <summary>
        /// 输出文件未找到
        /// </summary>
        /// <param name="context">HTTP请求信息</param>
        public static void ResponseFileNotFound(HttpContext context = null)
        {
            context = context ?? HttpContext.Current;
            context.Response.StatusCode = 404;
            context.Response.End();
        }
        #endregion

        #region 文件下载
        private static void ResponseFileBase(HttpContext context, Stream stream, string filename, long speed = 0)
        {
            context = context ?? HttpContext.Current;
            HttpRequest request = context.Request;
            HttpResponse response = context.Response;
            using (BinaryReader reader = new BinaryReader(stream))
            {
                long total = stream.Length;
                long start = 0;
                int pack = 10240;//10K 每包大小
                int sleep = 0;
                if (speed > 0)
                    sleep = (int)Math.Floor((double)1000 * pack / speed) + 1; //pack/speed 每个包发送速度/秒 总的意思:发包频率

                string range = request.Headers["Range"];
                if (!string.IsNullOrEmpty(range))
                {
                    response.StatusCode = 206;
                    start = Convert.ToInt64(range.Split(new char[] { '=', '-' })[1]);
                }
                long remain = total - start;
                response.AddHeader("Content-Length", remain.ToString());
                if (start != 0)
                {
                    //Content-Range: bytes [文件块的开始字节]-[文件的剩余大小]/[文件的总大小]
                    response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", start, remain, total));
                }
                response.AddHeader("Accept-Ranges", "bytes");
                response.ContentType = "application/octet-stream";
                response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(filename, Encoding.UTF8));
                response.AddHeader("Connection", "Keep-Alive");
                stream.Position = start;
                int maxCount = (int)Math.Floor((double)(total - start) / pack) + 1;
                for (int i = 0; i < maxCount; i++)
                {
                    if (response.IsClientConnected)
                    {
                        response.BinaryWrite(reader.ReadBytes(pack));
                        response.Flush();
                        if (speed > 0) Thread.Sleep(sleep);
                    }
                    else break;
                }
            }
        }

        /// <summary>
        /// 提供下载，支持断点续传
        /// </summary>
        /// <param name="stream">下载的数据流</param>
        /// <param name="filename">文件名</param>
        /// <param name="speed">每秒允许下载的字节数，0或负数为不限制</param>
        /// <param name="context">上下文</param>
        public static void ResponseFile(Stream stream, string filename, long speed = 0, HttpContext context = null)
        {
            ResponseFileBase(context, stream, filename, speed);
        }

        /// <summary>
        /// 提供文件下载，支持断点续传
        /// </summary>
        /// <param name="data">下载的数据</param>
        /// <param name="filename">下载的文件名</param>
        /// <param name="speed">每秒允许下载的字节数，0或小数为不限制</param>
        /// <param name="context">上下文</param>
        public static void ResponseFile(byte[] data, string filename, long speed = 0, HttpContext context = null)
        {
            ResponseFileBase(context, new MemoryStream(data), filename, speed);
        }

        /// <summary>
        /// 提供文件下载，支持断点续传
        /// </summary>
        /// <param name="downloadFile">提供下载的包含WEB相对路径或绝对路径的文件名</param>
        /// <param name="filename">下载的文件名</param>
        /// <param name="speed">每秒允许下载的字节数，0或小数为不限制</param>
        /// <param name="context">上下文</param>
        public static void ResponseFile(string downloadFile, string filename = null, long speed = 0, HttpContext context = null)
        {
            downloadFile = context.Server.MapPath(downloadFile);
            if (string.IsNullOrEmpty(filename))
                filename = Path.GetFileName(downloadFile);
            ResponseFileBase(context, new FileStream(downloadFile, FileMode.Open, FileAccess.Read), filename, speed);
        }


        #endregion

        /// <summary>
        /// 获得Web客户端的IP地址，并不总是能够获得Web客户端的真实IP
        /// </summary>
        /// <returns></returns>
        public static string GetClientIP()
        {
            string ret = string.Empty;
            HttpRequest request = HttpContext.Current.Request;
            if (request.ServerVariables["HTTP_VIA"] != null)
            {
                //透明代理(可获取真实IP) 普通匿名代理 欺骗性代理
                ret = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            }
            else  
            {
                //没有代理(可获取真实IP) 高匿名代理
                ret = request.ServerVariables["REMOTE_ADDR"];
            }

            if (ret != null)
            {
                string[] ips = ret.Split(',');
                if (ips != null && ips.Length > 0)
                    ret = ips[0];
            }
            //foreach (string ip in ips)
            //{
            //    if (StringUtil.IsIpAddress(ip) && NetUtil.GetIpMode(ip) == IpAddressMode.Public)
            //    {
            //        return ip;
            //    }
            //}
            return ret;
        }

        ///// <summary>
        ///// 将Request的集合信息(Params, Form, QueryString)映射为实体对象。
        ///// 请使用RequestMapperAttribute定义映射字段。如果未使用RequestMapperAttribute进行定义，则保证属性名与key保持一致。
        ///// </summary>
        ///// <typeparam name="T">映射类型</typeparam>
        ///// <param name="items">集合对象</param>
        ///// <returns></returns>
        //public static T MapTo<T>(this NameValueCollection items)
        //{
        //    return RequestMapper.Map<T>(items);
        //}

        #region 处理html代码
        /// <summary>
        /// 获得指定路径的HTML代码
        /// </summary>
        /// <param name="url">URL路径</param>
        /// <param name="encoding">网页使用的字符集</param>
        /// <returns></returns>
        public static string GetHtml(string url, Encoding encoding = null)
        {
            encoding = encoding ?? Encoding.UTF8;
            using (WebClient client = new WebClient())
            {
                using (StreamReader reader = new StreamReader(client.OpenRead(url), encoding))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        #endregion

        /// <summary>
        /// 判断文件类型是否为WEB格式图片(注：JPG,GIF,BMP,PNG)
        /// </summary>
        /// <param name="contentType">HTTP MIME 类型</param>
        /// <returns></returns>
        public static bool IsWebImage(string contentType)
        {
            return (contentType == "image/pjpeg" || contentType == "image/jpeg" || contentType == "image/gif" || contentType == "image/bmp" || contentType == "image/png" || contentType == "image/x-png");
        }

        #region Extensions
        /// <summary>
        /// 页面数据绑定的泛型方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="page"></param>
        /// <returns></returns>
        public static T Eval<T>(this Page page)
        {
            return (T)page.GetDataItem();
        }

        /// <summary>
        /// 用户控件数据绑定的泛型方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="control"></param>
        /// <returns></returns>
        public static T Eval<T>(this UserControl control)
        {
            return (T)control.Page.GetDataItem();
        }

        /// <summary>
        /// DropDownList绑定枚举类型
        /// </summary>
        /// <typeparam name="T">枚举类型</typeparam>
        /// <param name="ddl">绑定的DropDownList</param>
        public static void BindEnum<T>(this DropDownList ddl) where T : struct
        {
            Type enumType = typeof(T);
            if (!enumType.IsEnum) throw new ArgumentException("类型必须是枚举类型。");

            Dictionary<string, string> list = new Dictionary<string, string>();
            MemberInfo[] members = enumType.GetMembers();
            foreach (MemberInfo member in members)
            {
                if (member.IsDefined(typeof(DescriptionAttribute), false))
                {
                    object[] attrs = member.GetCustomAttributes(typeof(DescriptionAttribute), false);
                    string value = ((DescriptionAttribute)attrs[0]).Description;
                    string key = member.Name;
                    list.Add(key, value);
                }
            }
            ddl.DataSource = list;
            ddl.DataTextField = "Value";
            ddl.DataValueField = "Key";
            ddl.DataBind();
        }

        /// <summary>
        /// 获取枚举绑定的枚举对象
        /// </summary>
        /// <typeparam name="T">枚举类型</typeparam>
        /// <param name="ddl">绑定的DropDownList</param>
        /// <returns></returns>
        public static T SelectedEnum<T>(this DropDownList ddl) where T : struct
        {
            Type enumType = typeof(T);
            if (!enumType.IsEnum) throw new ArgumentException("类型必须是枚举类型。");
            return (T)Enum.Parse(enumType, ddl.SelectedValue);
        }

        #endregion
    }
}
